package com.oracle.coherence.patterns.security.accesscontrol;

import com.tangosol.net.ClusterPermission;
import com.tangosol.run.xml.XmlConfigurable;
import com.tangosol.run.xml.XmlElement;
import com.tangosol.run.xml.XmlHelper;

import javax.security.auth.Subject;
import java.io.File;

/**
 * This AccessController is used to secure Coherence cluster membership.
 * It uses configurable PermissionsChecker to actually perform the
 * authorisation.
 * <p/>
 */
public class ConfigurableClusterAccessController extends BaseClusterAccessController implements XmlConfigurable {

    /**
     * The permissions checker that will be used to verify permissions of subjects
     */
    private PermissionsChecker permissionsChecker;

    /**
     * The XML configuration used to configure this access controller
     */
    private XmlElement xmlConfig;

    /**
     * Empty constructor used in testing.
     */
    ConfigurableClusterAccessController() {
    }

    /**
     * Create a new KrbClusterAccessController using the keys from the specified keystore and
     * the specified permissions file.</p>
     * The specified permissions file is first looked for as a normal file, if there is no
     * file with the specified name the classpath is also searched. If the file is not found
     * on the classpath either then an exception will be thrown.</p>
     *
     * @param xmlConfig - the configuration of this access controller
     * @param _unused   - this parameter is unused but is provided by Coherence so cannot be removed
     */
    public ConfigurableClusterAccessController(XmlElement xmlConfig, File _unused) {
        setConfig(xmlConfig);
    }


    /**
     * Determine whether the cluster access request indicated by the specified permission
     * should be allowed or denied for a given Subject (requestor).<p/>
     * This method quietly returns if the access request is permitted, or throws a suitable
     * AccessControlException if the specified authentication is invalid or insufficient.<p/>
     *
     * @param clusterPermission - the permission object that represents access to a clustered resource
     * @param subject           - the Subject object representing the requestor
     * @throws java.security.AccessControlException
     *          - if the specified permission is not permitted,
     *          based on the current security policy
     */
    public void checkPermission(ClusterPermission clusterPermission, Subject subject) {
        permissionsChecker.checkPermission(clusterPermission, subject);
    }

    /**
     * @return the XML used to configure this access controller.
     */
    public XmlElement getConfig() {
        return xmlConfig;
    }

    /**
     * Set the PermissionsChecker that will be used by this AccessController
     * to verify permissions.
     * <p/>
     * This method is package protected as it is primarily for use in unit tests.
     * <p/>
     *
     * @param permissionsChecker the PermissionsChecker to use
     */
    void setPermissionsChecker(PermissionsChecker permissionsChecker) {
        this.permissionsChecker = permissionsChecker;
    }

    /**
     * get the PermissionsChecker that will be used by this AccessController
     * to verify permissions.
     * <p/>
     * This method is package protected as it is primarily for use in unit tests.
     * <p/>
     *
     * @return The PermissionsChecker being used by this AccessController.
     */
    PermissionsChecker getPermissionsChecker() {
        return permissionsChecker;
    }

    /**
     * Configure this access controller using the specified XML.<p/>
     *
     * @param xmlConfig - the XML to use to configure this access controller
     */
    public void setConfig(XmlElement xmlConfig) {
        this.xmlConfig = (XmlElement) xmlConfig.clone();

        XmlElement xmlKeystore = xmlConfig.getSafeElement("keystore");
        initKeystore(xmlKeystore.getSafeElement("filename").getString()
                , xmlKeystore.getSafeElement("username").getString()
                , xmlKeystore.getSafeElement("password").getString());

        XmlElement xmlPermissions = xmlConfig.getSafeElement("permissions-checker");
        this.permissionsChecker = (PermissionsChecker) XmlHelper.createInstance(xmlPermissions, null, null);
    }
}
